Skip to content

feat: user deletion functionality #2212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

m0wer
Copy link
Contributor

@m0wer m0wer commented Jun 7, 2025

Description

Implements account deletion functionality as requested in #603. This adds a comprehensive soft-delete system that allows users to permanently delete their accounts while preserving system integrity.

Closes #603

Key Features:

  • Soft delete with deletedAt timestamp to maintain referential integrity
  • User data anonymization (nullifies email, pubkey, auth tokens, etc.)
  • All user content is transferred to the special @delete user account
  • Content titles and bodies are replaced with "deleted by author" (using existing deletion logic)
  • Balance handling - users must withdraw or donate remaining sats/credits to rewards pool
  • Proper authentication checks to reject deleted accounts
  • UI integration in settings with confirmation flow
  • Username hashing to prevent unique constraint issues

Technical Implementation:

  • Added deletedAt field to User model with database migration
  • Created deleteAccount GraphQL mutation with proper validation
  • Modified authentication flow to check for deleted accounts in NextAuth callbacks
  • Updated zap distribution to handle deleted users (credits go to rewards pool)
  • Content from deleted users cannot be zapped (throws error)
  • Comprehensive transaction handling to ensure data consistency

Content Handling:

  • All user items are transferred to the special @delete user (USER_ID.delete = 106)
  • Content is processed through existing deleteItemByAuthor function
  • Titles and bodies are replaced with "deleted by author" text
  • No content hashing - uses standard deletion approach
  • Deleted content cannot be zapped or voted on

Authentication & Session Management:

  • NextAuth JWT callback checks for deleted accounts and prevents sign-in
  • Session callback validates user status on each request
  • Automatic logout after successful account deletion
  • SSR Apollo client handles deleted user states

Balance & Rewards:

  • Users must either withdraw balance or confirm donation to rewards pool
  • Donated balance is converted from msats to sats and added to donations table
  • Future zaps to deleted user content automatically go to rewards pool
  • Earnings from deleted users in zap distribution are donated to rewards pool

Screenshots

closed delete form
opened delete form
deleted comment

Additional Context

Key implementation decisions:

  1. Content Strategy: All content is transferred to @delete user and processed through existing deletion logic rather than being hashed or anonymized
  2. Balance Handling: Added option to donate remaining balance to rewards pool rather than requiring complete withdrawal
  3. Username Strategy: Hash username with deletion timestamp to prevent conflicts while maintaining anonymity
  4. Zap Protection: Content from deleted users cannot be zapped (throws error in zap.js)
  5. Rewards Distribution: Modified zap distribution to donate earnings that would go to deleted users to rewards pool

The authentication flow required updates in multiple places (NextAuth callbacks, SSR Apollo client, etc.) to properly handle deleted accounts across the application.

Checklist

Are your changes backwards compatible? Please answer below:
Yes, fully backwards compatible. Existing users are unaffected, and the deletedAt field is nullable. No breaking changes to existing APIs or database schema.

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
7/10 - Tested core deletion flow, authentication rejection, UI display, and balance handling. Would benefit from additional testing of edge cases around zap distribution to @delete user.

For frontend changes: Tested on mobile, light and dark mode? Please answer below:
Yes - The settings UI changes were tested on mobile and dark mode.

Did you introduce any new environment variables? If so, call them out explicitly here:
No new environment variables introduced. Uses existing USER_ID.delete constant for the special delete user account.

@m0wer m0wer requested a review from huumn June 10, 2025 15:38
@m0wer m0wer marked this pull request as ready for review June 10, 2025 15:38
@m0wer
Copy link
Contributor Author

m0wer commented Jun 10, 2025

BTW the lint error is unfixable! (take it as a challenge) It looks like the linter gets confused with the ternary operator inside the nested JSX structure. If there's no way to fix it, we can just disable the check for those lines.

@m0wer
Copy link
Contributor Author

m0wer commented Jun 17, 2025

Looking for QA 👀

@huumn
Copy link
Member

huumn commented Jun 17, 2025

@m0wer this is going to take a minute. we are all kind of swamped and this is a big, potentially dangerous (although I'm sure it's fine) change.

i'd guess you'll see a review when one of us needs a break from our own work. i personally might not be able to get to this for a couple weeks given some real life stuff.

just wanted to give a heads up. this is something we want, so we'll get around to it eventually.

Copy link
Member

@ekzyis ekzyis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick review by looking at the code. I did not run or test the code.

@m0wer m0wer requested a review from ekzyis July 6, 2025 20:20
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@m0wer
Copy link
Contributor Author

m0wer commented Jul 9, 2025

@ekzyis ready for another round of QA!

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Null Item Handling Error

A missing null check for item causes a TypeError when tx.item.findUnique returns null (i.e., the item does not exist). This occurs when attempting to access item.userId, preventing the intended "cannot zap deleted content" error.

api/paidAction/zap.js#L59-L63

export async function perform ({ invoiceId, sats, id: itemId, ...args }, { me, cost, sybilFeePercent, tx }) {
const item = await tx.item.findUnique({ where: { id: parseInt(itemId) } })
if (item.userId === USER_ID.delete) {
throw new Error('cannot zap deleted content')
}

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Account Deletion Feature Request
4 participants